/**
 * WTF Engine
 *
 * License... etc.
 **
 * SDL Graphics Implementation
 **
 * Authors: Dan Horgan, Sean Micklethwaite
 **/

#include "config.h"

// Only compile if using SDL
#ifdef __WM_SDL__

#include "math.h"
#include "sdlmisc.h"

#include "graphics.h"
#include <iostream>
#include "glext.h"
#include <SDL_syswm.h>



using namespace WtfGraphics;
Screen *	Screen::MainScreen;


// _TEST_ : OpenGL lights
float LightAmbient[]= { 1.0f, 1.0f, 1.0f, 0.0f};
float LightDiffuse[]= { 0.5f, 0.6f, 0.6f, 1.0f};
float LightPosition[]= { -10.0f, 20.0f, 100.0f, 1.0f};
float LightSpecular[]= {0.5f, 0.6f, 0.5f , 1.0f};



// CANVAS CLASS
// Constructor
Canvas::Canvas()
: WtfEngine::DataObject("Canvas") {
	this->surface = NULL;
}

Canvas::Canvas(iVector2D dim)
: WtfEngine::DataObject("Canvas") {
	this->surface = NULL;
}

Canvas::Canvas(SDL_Surface *s)
: WtfEngine::DataObject("Canvas") {
	this->surface = s;
}

// Destructor
Canvas::~Canvas() {
	this->Unload();
}

// get dimensions of canvas
iVector2D Canvas::dim() const { return iVector2D(this->surface->w, this->surface->h); }

SDL_Surface *Canvas::getSurface() {return this->surface;}


// Sets the destination rect such that the image would be drawn centered to the *surface
/*void Canvas::CenterDestRect(SDL_Surface *surface) {
    this->drect.x = (surface->w - this->surface->w)/2;
    this->drect.y = (surface->h - this->surface->h)/2;
}

void Canvas::SetDestPosition(int x, int y) {
	this->drect.x = x;
	this->drect.y = y;
}

// draws on the selected surface
void Canvas::DrawOn(SDL_Surface *surface) {
    SDL_BlitSurface(this->surface,&this->srect,surface,&this->drect);
}

// draws on the selected surface at given position
void Canvas::DrawOnAt(SDL_Surface *surface, int x, int y) {
	SetDestPosition(x, y);
	DrawOn(surface);
}*/

// copy another canvas onto this one
/*void Canvas::putCanvas(Canvas *c, iVector2D pos)
{
};

// plot a pixel of a certain colour to the surface
void Canvas::putPixel(iVector2D pos, Colour c)
{
	
};

// draw a circle of radius r
void Canvas::putCircle(iVector2D pos, tScalar r, Colour c)
{
	
};

// draw a line segment
void Canvas::putLine(iLineSegment *l, Colour c)
{
};

// draw a straight line segment from (x1, y1) to (x2, y2), smoothly changing between color1 and color2
void Canvas::putGradientLine(iLineSegment l, Colour c1, Colour c2)
{
	
};
*/

// loads and image and automatically sets the source rect to cover the whole picture.
// By default, the picture is aligned to 0,0 on the destination surface
void Image::LoadBMP(char *path) {
	// Unload existing image before trying to load another
	this->Unload();
	this->surface = SDL_LoadBMP(path);

    /*this->srect.x=0;
    this->srect.y=0;
    this->srect.w=Surface->w;
	this->srect.h=Surface->h;
	this->drect = this->srect;*/
};

// Unloads the image, if one is loaded
void Canvas::Unload() {
	if(this->surface)
		SDL_FreeSurface(this->surface);
};

// initialize OpenGL
void Screen::InitGL()
{
	// Shading
	glShadeModel(GL_SMOOTH);
	glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

	glClearColor(1.0f, 1.0f, 1.0f, 0.0f);
	glClearDepth(1.0f);
	
	// Features
	glEnable(GL_DEPTH_TEST);
    glDepthFunc(GL_LESS);

	glEnable (GL_POLYGON_SMOOTH);


	clearScreen();
	SDL_GL_SwapBuffers();
	
	glViewport(0,0, this->dim().x(), this->dim().y());
	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();
	

	// initial camera setup
//	glOrtho(0.0f, this->dim().x(), this->dim().y(), 0.0f, -1.0f, 1.0f);
	gluPerspective(45.0f,(tScalar)(this->dim().x())/(tScalar)(this->dim().y()),0.1f,2000.0f);
	//gluLookAt(0,500.0f,-800.0f,0.0f,0.0f,0.0f,0.0f,1.0f,0.0f);


	// Light settings
	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);
	glLightfv(GL_LIGHT0, GL_AMBIENT, LightAmbient);
	glLightfv(GL_LIGHT0, GL_DIFFUSE, LightDiffuse);
	glLightfv(GL_LIGHT0, GL_SPECULAR, LightSpecular);
	glLightfv(GL_LIGHT0, GL_POSITION, LightPosition);
	glDisable(GL_LIGHTING);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
};

void Screen::clearScreen()
{
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
};

int Screen::getWindowHandle() const {
	SDL_SysWMinfo info;
	SDL_GetWMInfo(&info);
	return (int)info.window;
};


/**
 * Screen mode initialization
 **/
void Screen::SetMode(const GraphicsMode& mode)
{
	// enable opengl - this is required
	int videoFlags = SDL_OPENGL;
	
	// enable depth testing
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);

	// enable gl double buffering if requested
	if (mode.uNumBuffers == 2)
	{
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		videoFlags |= SDL_GL_DOUBLEBUFFER;
	} else if(mode.uNumBuffers != 1) {
		throw new std::runtime_error("Unsupported number of back buffers");
	};

	// use hardware surfaces if requested
	if (mode.bHwAccel)
	{
		videoFlags |= SDL_HWSURFACE | SDL_HWACCEL | SDL_HWPALETTE;
	}
	else // otherwise use software surfaces
	{
		videoFlags |= SDL_SWSURFACE;
	}

	// enable fullscreen if requested
	if (mode.WindowStyle == mode.FULLSCREEN) {
		videoFlags |= SDL_FULLSCREEN;
	} else if (mode.WindowStyle == mode.RESIZABLE) {
		// enable window to be resized, if set
		videoFlags |= SDL_RESIZABLE;
	} else if(mode.WindowStyle == mode.BORDERLESS) {

	};

	unsigned bpp = mode.uBitsPerPixel;

	// Check mode with SDL
	unsigned bppCheck = SDL_VideoModeOK(mode.Resolution[0], mode.Resolution[1], bpp, videoFlags);
	if(!bppCheck)
		throw new std::runtime_error("The requested video mode is not available at any bit depth.");

	if(bppCheck != bpp)
		WtfEngine::Kernel::DebugPrint("Could not use requested bit depth, but attempting to continue.");	

	bpp = bppCheck;

	this->surface = SDL_SetVideoMode(mode.Resolution[0], mode.Resolution[1], mode.uBitsPerPixel, videoFlags);
	if (this->surface == NULL)
		throw new std::runtime_error("Failed to create SDL screen");

#ifdef __GRAPHICS_OPENGL__
	InitGL();
#endif
};

void Screen::UpdateScreen()
{
	SDL_GL_SwapBuffers();
};

void Screen::Activate() {
	// Stuff to do before rendering
};

void Screen::Deactivate() {
	SDL_GL_SwapBuffers();
};


//default constructor
GraphicsMode::GraphicsMode()
{
	this->Resolution = iVector2D(1024, 768);
	this->uBitsPerPixel = 32;
	this->bHwAccel = true;
	this->uNumBuffers = 2;
	this->WindowStyle = FIXED;
};


#endif
